#include "Avalon/IO/Channel.hpp"
#include "Avalon/IO/Connection.hpp"
#include "Avalon/IO/Reader.hpp"
#include "Avalon/IO/Writer.hpp"

using namespace Avalon;
using namespace Avalon::IO;
using namespace boost;

namespace {
  struct ReadWriteCreator {
    ReadWriteCreator(WrapperChannelFactory::ReadWrapper readWrapper,
        WrapperChannelFactory::WriteWrapper writeWrapper)
        : m_readWrapper(readWrapper),
          m_writeWrapper(writeWrapper) {}

    Channel* operator()(Carry<Connection> connection, Carry<Reader> reader,
        Carry<Writer> writer) const {
      return new BasicChannel(connection, m_readWrapper(reader),
        m_writeWrapper(writer));
    }

    Channel* operator()(Carry<Channel> channel) const {
      return new BasicChannel(channel, channel->GetConnection(),
        m_readWrapper(channel->GetReader()),
        m_writeWrapper(channel->GetWriter()));
    }

    WrapperChannelFactory::ReadWrapper m_readWrapper;
    WrapperChannelFactory::WriteWrapper m_writeWrapper;
  };
}

Channel::~Channel() {}

BasicChannel::BasicChannel(Carry<Connection> connection, Carry<Reader> reader,
    Carry<Writer> writer)
    : m_connection(connection),
      m_reader(reader),
      m_writer(writer) {}

BasicChannel::BasicChannel(Carry<Channel> channel,
    Carry<Connection> connection, Carry<Reader> reader, Carry<Writer> writer)
    : m_channel(channel),
      m_connection(connection),
      m_reader(reader),
      m_writer(writer) {}

BasicChannel::~BasicChannel() {}

Connection& BasicChannel::GetConnection() {
  return *m_connection;
}

Reader& BasicChannel::GetReader() {
  return *m_reader;
}

Writer& BasicChannel::GetWriter() {
  return *m_writer;
}

ChannelFactory::~ChannelFactory() {}

Channel* ChannelFactory::Create(Carry<Connection> connection,
    Carry<Reader> reader, Carry<Writer> writer) {
  return Create(new BasicChannel(connection, reader, writer));
}

Channel* ChannelFactory::Create(Channel* channel) {
  Channel* componentsChannel = Create(channel->GetConnection(),
    channel->GetReader(), channel->GetWriter());
  return new BasicChannel(componentsChannel,
    componentsChannel->GetConnection(), componentsChannel->GetReader(),
    componentsChannel->GetWriter());
}

WrapperChannelFactory::WrapperChannelFactory(ChannelCreator channelCreator)
    : m_channelCreator(channelCreator),
      m_hasChannelCreator(true),
      m_hasComponentCreator(false) {}

WrapperChannelFactory::WrapperChannelFactory(ComponentCreator componentCreator)
    : m_hasChannelCreator(false),
      m_componentCreator(componentCreator),
      m_hasComponentCreator(true) {}

WrapperChannelFactory::WrapperChannelFactory(ReadWrapper readWrapper,
    WriteWrapper writeWrapper)
    : m_hasChannelCreator(false),
      m_componentCreator(ReadWriteCreator(readWrapper, writeWrapper)),
      m_hasComponentCreator(true) {}

WrapperChannelFactory::~WrapperChannelFactory() {}

Channel* WrapperChannelFactory::Create(Carry<Connection> connection,
    Carry<Reader> reader, Carry<Writer> writer) {
  if(!m_hasComponentCreator) {
    return ChannelFactory::Create(connection, reader, writer);
  }
  return m_componentCreator(connection, reader, writer);
}

Channel* WrapperChannelFactory::Create(Channel* channel) {
  if(!m_hasChannelCreator) {
    return ChannelFactory::Create(channel);
  }
  return m_channelCreator(channel);
}
